package com.wngbms.designpattern;

import javax.transaction.InvalidTransactionException;
import java.util.UUID;

/**
 * 本类属于DDD
 */
public class Transaction {
    private String id;
    private Long buyerId;
    private Long sellerId;
    private Long productId;
    private String orderId;
    private Long createTimestamp;
    private Double amount;
    private STATUS status;
    private String walletTransactionId;

    WalletRpcService walletRpcService;
    TransactionLock transactionLock;

    public Transaction(String preAssignedId, Long buyerId,
                       Long sellerId, Long productId, String orderId, Double amount) {
        if (preAssignedId != null && !preAssignedId.isEmpty()) {
            this.id = preAssignedId;
        } else {
            this.id = IdGenerator.generateTransactionId();
        }
        if (!this.id.startsWith("t_")) {
            this.id = "t_" + preAssignedId;
        }
        this.buyerId = buyerId;
        this.sellerId = sellerId;
        this.productId = productId;
        this.orderId = orderId;
        this.status = STATUS.TO_BE_EXECUTD;
        this.createTimestamp = System.currentTimeMillis();
        this.amount = amount;
    }

    public boolean execute() throws InvalidTransactionException {
        if (buyerId == null || (sellerId == null || amount < 0.0D)) {
            throw new InvalidTransactionException("");
        }

        if (status == STATUS.EXECUTED) return true;
        boolean isLocked = false;

        try {
//            isLocked = RedisDistributedLock.getSingletonIntance().lockTransction(id);
            isLocked = transactionLock.lock(id);
            if (!isLocked) {
                return false; // 锁定未成功，返回false，job兜底执行
            }

            if (status == STATUS.EXECUTED) return true; // double check

//            long executionInvokedTimestamp = System.currentTimeMillis();
//            if (executionInvokedTimestamp - createTimestamp > 14d) {
            if (isExpired()) {
                this.status = STATUS.EXPIRED;
                return false;
            }

            String walletTransactionId = walletRpcService.moveMoney(id, buyerId, sellerId, amount);

            if (walletTransactionId != null) {
                this.walletTransactionId = walletTransactionId;
                this.status = STATUS.EXECUTED;
                return true;
            } else {
                this.status = STATUS.FAILED;
                return false;
            }

        } finally {
            if (isLocked) {
//                RedisDistributedLock.getSingletonIntance().unlockTransction(id);
                transactionLock.unlock(id);
            }
        }
    }

    /**
     * ********** 未决行为. **********
     *
     * @return
     */
    protected boolean isExpired() {
        long executionInvokedTimestamp = System.currentTimeMillis();
        return executionInvokedTimestamp - createTimestamp > 14d;
    }

    public void setTransactionLock(TransactionLock transactionLock) {
        this.transactionLock = transactionLock;
    }

    /**
     * ************* 依赖注入 *******************
     *
     * @param walletRpcService
     */
    public void setWalletRpcService(WalletRpcService walletRpcService) {
        this.walletRpcService = walletRpcService;
    }

    public STATUS getStatus() {
        return status;
    }

    public enum STATUS {
        TO_BE_EXECUTD,
        EXECUTED,
        EXPIRED,
        FAILED,
        ;
    }

    public static class IdGenerator {
        public static String generateTransactionId() {
            return UUID.randomUUID().toString();
        }
    }

    public static class RedisDistributedLock {
        public static RedisDistributedLock getSingletonIntance() {
            return new RedisDistributedLock();
        }

        public boolean lockTransction(String id) {
            return false;
        }

        public void unlockTransction(String id) {
        }
    }

    public static class WalletRpcService {
        public String moveMoney(String id, Long buyerId, Long sellerId, Double amount) {
            throw new UnsupportedOperationException();
        }
    }

    public static class MockWalletRpcServiceOne extends WalletRpcService {
        @Override
        public String moveMoney(String id, Long buyerId, Long sellerId, Double amount) {
            return UUID.randomUUID().toString();
        }
    }

    public static class MockWalletRpcServiceTwo extends WalletRpcService {
        @Override
        public String moveMoney(String id, Long buyerId, Long sellerId, Double amount) {
            return null;
        }
    }

}